﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Reflection;
using System.Text;
using System.Diagnostics;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using RestSharp;

namespace Ymatou.ServiceCenter
{
    /// <summary>
    /// 请求参数包装，以便各方法中共用
    /// </summary>
    internal class RequestParameter
    {
        /// <summary>
        /// Rest请求参数包
        /// </summary>
        public RestRequest RestRequest { get; set; }

        /// <summary>
        /// 服务元数据
        /// </summary>
        public ServiceMetadata ServiceMetadata { get; set; }

        /// <summary>
        /// 服务资源特性标签
        /// </summary>
        public ResourceAttribute ResourceAttribute { get; set; }

        /// <summary>
        /// 请求编号
        /// </summary>
        public string RequestId { get; set; }

        /// <summary>
        /// 当前请求路径
        /// </summary>
        public string CurrentRequestPath { get; set; }        

        /// <summary>
        /// 请求顺序编号
        /// </summary>
        public int RequestIndex { get; set; }

        /// <summary>
        /// 服务消费者应用编号
        /// </summary>
        public string ConsumerAppId { get; set; }

        /// <summary>
        /// 服务提供者的应用编号
        /// </summary>
        public string ProviderAppId { get; set; }

        /// <summary>
        /// 服务调用监控路径
        /// </summary>
        /// <returns></returns>
        public string GetMethodMonitorPath()
        {
            return string.Format("{0}->{1}({2})", this.ConsumerAppId, this.ProviderAppId, this.ServiceMetadata.ApiName);
        }
    }

    /// <summary>
    /// 服务调用的统一客户端
    /// </summary>
    public static class ServiceClient
    {
        static ServiceClient()
        {
            ServicePointManager.UseNagleAlgorithm = false;
            ServicePointManager.Expect100Continue = false;
            ServicePointManager.DefaultConnectionLimit = 512;
        }

        /// <summary>
        /// 默认的超时时间
        /// </summary>
        internal  static int DefaultTimeOut = 60000;

        internal static int GetDefaultTimeOut()
        {
            return DefaultTimeOut <= 0 ? 60000 : DefaultTimeOut;
        }

        /// <summary>
        /// 获取资源特性标签，如果AppId参数不为空，则用这个参数值替换原来的值
        /// </summary>
        /// <typeparam name="TRequest">Request参数类型</typeparam>
        /// <param name="appId">应用编号</param>
        /// <param name="request">请求包</param>
        /// <returns></returns>
        private static ResourceAttribute GetResourceAttribute<TRequest>(string appId, TRequest request)
        {
            ResourceAttribute resourceAttribute = null;
            if (!string.IsNullOrWhiteSpace(appId))
            {
                if (request != null && request is IResource)
                {
                    resourceAttribute = ((IResource) request).GetResourceAttribute();
                }

                if (resourceAttribute == null)
                {
                    DefaultRequest defaultRequest = request as DefaultRequest;
                    if (defaultRequest != null)
                    {
                        resourceAttribute = defaultRequest.ResourceAttribute;
                    }
                }

                if(resourceAttribute==null)
                    resourceAttribute = Utils.GetFirstAttribute<ResourceAttribute>(typeof(TRequest), true);
                
                if (resourceAttribute != null)
                    resourceAttribute.AppId = appId;
            }
            return resourceAttribute;
        }

        /// <summary>
        /// 保存服务依赖关系
        /// </summary>
        /// <param name="requestParameter">请求参数包</param>
        /// <param name="requestElapsedMilliseconds">请求耗时的毫秒数</param>
        /// <param name="isFail">请求是否执行失败</param>
        private static void SaveServiceDependency(RequestParameter requestParameter, long requestElapsedMilliseconds,
            bool isFail)
        {
            if (requestParameter == null)
                return;
            ThreadPool.QueueUserWorkItem((state) =>
            {
                try
                {
                    if (!string.IsNullOrEmpty(requestParameter.ConsumerAppId) &&
                        !string.IsNullOrEmpty(requestParameter.ProviderAppId) &&
                        requestParameter.ConsumerAppId != requestParameter.ProviderAppId)
                    {
                        ServiceDependencyRequest serviceDependencyRequest = new ServiceDependencyRequest
                        {
                            ConsumerAppId = requestParameter.ConsumerAppId,
                            ProviderAppId = requestParameter.ProviderAppId,
                            ApiName = requestParameter.ServiceMetadata.ApiName,
                            ApiPath = requestParameter.ServiceMetadata.ApiPath,
                            RequestElapsedMilliseconds = requestElapsedMilliseconds,
                            IsFail = isFail
                        };
                        ServiceDependencyStorage.Save(serviceDependencyRequest);
                    }
                }
                catch (Exception ex)
                {
                    WriteLog(requestParameter, ex);
                }

            }, null);
        }

        /// <summary>
        /// 写日志
        /// </summary>
        /// <param name="requestParameter">请求参数包</param>
        /// <param name="exception">异常信息</param>
        private static void WriteLog(RequestParameter requestParameter, Exception exception = null)
        {
            try
            {
                if (requestParameter == null || requestParameter.ServiceMetadata == null)
                    return;
                StringBuilder sb = new StringBuilder();
                sb.AppendFormat("ServiceClient,[RequestId]:{0},[RequestIndex]:{1},ConsumerAppId:{2}->ProviderAppId:{3} (ApiName:{4},ApiPath:{5},Host:{6},HttpMethod:{7},parameterBuildType:{8}),ConsumerAppIp:{9}", requestParameter.RequestId,
                  requestParameter.RequestIndex, requestParameter.ConsumerAppId, requestParameter.ProviderAppId, requestParameter.ServiceMetadata.ApiName, requestParameter.ServiceMetadata.ApiPath,
                  requestParameter.ServiceMetadata.Host, requestParameter.ServiceMetadata.HttpMethod, requestParameter.ResourceAttribute.ParameterBuildType.ToString(), Utils.LocalIp);
                if (exception == null)
                {
                    Logger.Debug(sb.ToString());
                }
                else
                {
                    Logger.Error(sb.ToString(), exception);
                }
            }
            catch
            {
                // ignored
            }
        }

        /// <summary>
        /// 从ResourceAttribute中构建ServiceMetadata
        /// </summary>
        /// <param name="resourceAttribute"></param>
        private static ServiceMetadata ResourceAttributeToServiceMetadata(ResourceAttribute resourceAttribute)
        {
            if (resourceAttribute == null)
                return null;
            ServiceMetadata serviceMetadata = new ServiceMetadata
            {
                AppId = resourceAttribute.AppId,
                ApiName = resourceAttribute.ApiName,
                ApiPath = resourceAttribute.ApiName,
                Host = resourceAttribute.AppId,
                HttpMethod = resourceAttribute.HttpMethod.ToString()
            };
            if (!string.IsNullOrWhiteSpace(serviceMetadata.AppId))
            {
                serviceMetadata.AppId = serviceMetadata.AppId.ToLower();
                serviceMetadata.AppId = serviceMetadata.AppId.TrimStart("http://".ToCharArray());
                serviceMetadata.AppId = serviceMetadata.AppId.TrimStart("https://".ToCharArray());
            }
            if (!string.IsNullOrWhiteSpace(serviceMetadata.Host) && !serviceMetadata.Host.ToLower().StartsWith("http"))
            {
                serviceMetadata.Host = string.Concat("http://", serviceMetadata.Host);
            }
            return serviceMetadata;
        }

        /// <summary>
        /// 构建请求参数包
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <typeparam name="TResponse">响应参数类型</typeparam>
        /// <param name="request">请求参数</param>
        /// <param name="headers">http头信息</param>
        /// <param name="resourceAttribute">服务资源特性标签</param>
        /// <returns></returns>
        private static RequestParameter BuildRequestParameter<TRequest, TResponse>(TRequest request,
            IDictionary<string, string> headers = null, ResourceAttribute resourceAttribute = null)
        {
            var validate = request as IValidate;
            if (validate != null)
            {
                validate.Validate();
            }

            Type requestType = typeof(TRequest);
            DefaultRequest defaultRequest = request as DefaultRequest;
            if (resourceAttribute == null && request is IResource)
            {
                resourceAttribute = ((IResource)request).GetResourceAttribute();
            }
            if (resourceAttribute == null && defaultRequest!=null)
            {
                resourceAttribute = defaultRequest.ResourceAttribute;
            }
            if (resourceAttribute == null)
            {
                resourceAttribute = Utils.GetFirstAttribute<ResourceAttribute>(requestType, true);
            }
            
            if (resourceAttribute == null)
                throw new ArgumentNullException("resourceAttribute");

            if (string.IsNullOrWhiteSpace(resourceAttribute.AppId))
                throw new ArgumentNullException("resourceAttribute.AppId");
            if (string.IsNullOrWhiteSpace(resourceAttribute.ApiName))
                throw new ArgumentNullException("resourceAttribute.ApiName");

            RequestParameter requestParameter = new RequestParameter { ResourceAttribute = resourceAttribute };
            ServiceMetadata serviceMetadata = ServiceMetadataManager.GetServiceMetadata(resourceAttribute.AppId, resourceAttribute.ApiName, resourceAttribute.HttpMethod);
            if (serviceMetadata == null)
            {
                serviceMetadata = ResourceAttributeToServiceMetadata(resourceAttribute);
                if (serviceMetadata==null)
                    throw new Exception(string.Format("没有找到元数据信息：AppId:{0},ApiName:{1},HttpMethod:{2}", resourceAttribute.AppId, resourceAttribute.ApiName, resourceAttribute.HttpMethod));
            }
            if (string.IsNullOrWhiteSpace(serviceMetadata.ApiPath))
            {
                throw new Exception(string.Format("元数据信息：AppId:{0},ApiName:{1},HttpMethod:{2} 中的信息ApiPath为空!", resourceAttribute.AppId, resourceAttribute.ApiName, resourceAttribute.HttpMethod));
            }
            string consumerAppId = ConfigurationManager.AppSettings["AppId"];
            if(string.IsNullOrWhiteSpace(consumerAppId))
                throw new ArgumentNullException("AppId");
            string providerAppId = serviceMetadata.AppId;

            Method httpMethod = (Method)Enum.Parse(typeof(Method), serviceMetadata.HttpMethod, true);
            RestRequest restRequest = new RestRequest { Resource = serviceMetadata.ApiPath, Method = httpMethod };
            if (resourceAttribute.ParameterBuildType == ParameterBuildType.Json)
            {
                restRequest = new JsonRequest { Resource = serviceMetadata.ApiPath, Method = httpMethod };
            }
            if (defaultRequest != null)
            {
                defaultRequest.RestRequest.Resource = serviceMetadata.ApiPath;
                defaultRequest.RestRequest.Method = httpMethod;
                restRequest = defaultRequest.RestRequest;
            }
            string requestId = HttpContext.Current == null ? null : HttpContext.Current.Request.Headers.Get("RequestId");
            string currentRequestPath = HttpContext.Current == null ? null : HttpContext.Current.Request.Path;

            if (string.IsNullOrEmpty(requestId) && headers != null && headers.ContainsKey("RequestId"))
            {
                requestId = headers["RequestId"];
            }
            if (string.IsNullOrEmpty(requestId))
            {
                requestId = Guid.NewGuid().ToString("N");
            }
            restRequest.AddHeader("RequestId", requestId);

            ServiceConfig serviceConfig = ServiceConfigManager.GetServiceConfig(consumerAppId, providerAppId);
            if (serviceConfig != null && serviceConfig.IsDisabled == true)
                throw new ServiceClientException(requestId, string.Format("应用：{0}， 无权访问应用：{1}", consumerAppId, providerAppId));

            string requestIndex = HttpContext.Current == null ? null : HttpContext.Current.Request.Headers.Get("RequestIndex");
            if (string.IsNullOrEmpty(requestIndex) && headers != null && headers.ContainsKey("RequestIndex"))
            {
                requestIndex = headers["RequestIndex"];
            }
            if (string.IsNullOrWhiteSpace(requestIndex))
            {
                requestParameter.RequestIndex = 1;
            }
            else
            {
                int index = 1;
                int.TryParse(requestIndex, out index);
                index = index + 1;
                requestParameter.RequestIndex = index;
            }
            restRequest.AddHeader("RequestIndex", requestParameter.RequestIndex.ToString());
            string clientIp = Utils.GetWebClientIp(HttpContext.Current == null ? null : HttpContext.Current.Request);
            restRequest.AddHeader("ClientIp",string.IsNullOrWhiteSpace(clientIp)?Utils.LocalIp:clientIp);
            restRequest.AddHeader("ServerIpLink", Utils.GetServerIpLink(HttpContext.Current == null ? null : HttpContext.Current.Request));
            if (headers != null)
            {
                foreach (KeyValuePair<string, string> pair in headers)
                {
                    if (pair.Key == "RequestId" || pair.Key == "RequestIndex")
                        continue;
                    restRequest.AddHeader(pair.Key, pair.Value);
                }
            }

            if (request != null && defaultRequest==null)
            {
                
                PropertyInfo[] propertyInfos = request.GetType().GetProperties();
                switch (resourceAttribute.ParameterBuildType)
                {
                    case ParameterBuildType.UrlQueryString:
                        foreach (var propertyInfo in propertyInfos)
                        {
                            object value = propertyInfo.GetValue(request, null);
                            string strValue = value == null ? null : value.ToString();
                            if (serviceMetadata.ApiPath.Contains("{") && serviceMetadata.ApiPath.Contains("}"))
                                restRequest.AddUrlSegment(propertyInfo.GetName(), strValue);
                            else
                                restRequest.AddQueryParameter(propertyInfo.GetName(), strValue);
                        }
                        break;
                    case ParameterBuildType.FormUrlEncoded:
                        foreach (var propertyInfo in propertyInfos)
                        {
                            object value = propertyInfo.GetValue(request, null);
                            string strValue = value == null ? null : value.ToString();
                            restRequest.AddParameter(propertyInfo.GetName(), strValue);
                            BodyStringValueAttribute bodyStringValueAttribute = Utils.GetFirstAttribute<BodyStringValueAttribute>(propertyInfo, true);
                            if (bodyStringValueAttribute != null)
                                restRequest.AddParameter("", strValue);
                        }
                        break;
                    case ParameterBuildType.QueryOrForm:
                        foreach (var propertyInfo in propertyInfos)
                        {
                            object value = propertyInfo.GetValue(request, null);
                            string strValue = value == null ? null : value.ToString();
                            QueryParameterAttribute queryParameterAttribute =
                                Utils.GetFirstAttribute<QueryParameterAttribute>(propertyInfo, true);
                            if (queryParameterAttribute != null)
                            {
                                string para = string.Concat("{", propertyInfo.GetName(), "}");
                                if (serviceMetadata.ApiPath.IndexOf(para, StringComparison.CurrentCultureIgnoreCase) >= 0)
                                    restRequest.AddUrlSegment(propertyInfo.GetName(), strValue);
                                else
                                    restRequest.AddQueryParameter(propertyInfo.GetName(), strValue);
                            }
                            FormParameterAttribute formParameterAttribute =
                                Utils.GetFirstAttribute<FormParameterAttribute>(propertyInfo, true);
                            if (formParameterAttribute != null)
                                restRequest.AddParameter(propertyInfo.GetName(), strValue);

                            BodyStringValueAttribute bodyStringValueAttribute = Utils.GetFirstAttribute<BodyStringValueAttribute>(propertyInfo, true);
                            if (bodyStringValueAttribute != null)
                                restRequest.AddParameter("", strValue);
                        }

                        break;
                    case ParameterBuildType.Json:
                        restRequest.AddJsonBody(request);
                        break;
                }

            }
            requestParameter.ConsumerAppId = consumerAppId;
            requestParameter.ProviderAppId = providerAppId;
            requestParameter.RequestId = requestId;
            requestParameter.CurrentRequestPath = currentRequestPath;
            requestParameter.RestRequest = restRequest;
            requestParameter.ServiceMetadata = serviceMetadata;
            return requestParameter;
        }

#if Net45
        /// <summary>
        ///  以Task异步的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <typeparam name="TResponse">响应参数类型</typeparam>
        /// <param name="request">请求参数</param>
        /// <param name="exceptionResult">当请求发生异常时返回指定的响应值的委托方法</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        /// <param name="resourceAttribute">服务资源特性标签</param>
        /// <returns></returns>
        public static async Task<TResponse> ExecuteTaskAsync<TRequest, TResponse>(TRequest request, Func<Exception, TResponse> exceptionResult = null, int? timeOut = null, IDictionary<string, string> headers = null, ResourceAttribute resourceAttribute = null)
        {
            if (request == null)
                throw new ArgumentNullException("request");
            Stopwatch watch = new Stopwatch();
            Type responseType = typeof(TResponse);
            bool isFail = false;
            RequestParameter requestParameter = null;
            try
            {
                requestParameter = BuildRequestParameter<TRequest, TResponse>(request, headers, resourceAttribute);
                watch.Start();
                if (timeOut == null)
                    timeOut = GetDefaultTimeOut();
                //using (MethodMonitor.New(requestParameter.GetMethodMonitorPath()))
                //{
                    RestClient restClient = new RestClient(requestParameter.ServiceMetadata.Host) { Timeout = timeOut.Value };
                    restClient.UseJsonDotNetDeserializerWrapper();
                    restClient.UserAgent = "Ymatou.ServiceCenter";
                    if (typeof(TextResponse).IsAssignableFrom(responseType))
                    {
                        return await restClient.ExecuteTaskAsync(requestParameter.RestRequest).ContinueWith(task =>
                        {
                            try
                            {
                                task.Result.CheckResponse();
                                object obj = Activator.CreateInstance(responseType, true);
                                ((TextResponse)obj).Result = task.Result.Content;
                                return (TResponse)obj;
                            }
                            catch (Exception ex)
                            {
                                if (exceptionResult != null)
                                {
                                    return exceptionResult(ex);
                                }
                                else
                                {
                                    ServiceClientException serviceClientException = new ServiceClientException(requestParameter == null ? "" : requestParameter.RequestId, ex.Message, ex);
                                    throw serviceClientException;
                                }
                            }
                            finally
                            {
                                watch.Stop();
                            }

                        });
                    }
                    else
                    {
                        return await restClient.ExecuteTaskAsync<TResponse>(requestParameter.RestRequest).ContinueWith(task =>
                        {
                            try
                            {
                                task.Result.CheckResponse();
                                return task.Result.Data;
                            }
                            catch (Exception ex)
                            {
                                if (exceptionResult != null)
                                {
                                    return exceptionResult(ex);
                                }
                                else
                                {
                                    ServiceClientException serviceClientException = new ServiceClientException(requestParameter == null ? "" : requestParameter.RequestId, ex.Message, ex);
                                    throw serviceClientException;
                                }
                            }
                            finally
                            {
                                watch.Stop();
                            }
                        });
                    }
                //}

            }
            catch (Exception ex)
            {
                isFail = true;
                if (requestParameter != null)
                    WriteLog(requestParameter, ex);

                if (exceptionResult != null)
                {
                    return exceptionResult(ex);
                }
                else
                {
                    ServiceClientException serviceClientException = new ServiceClientException(requestParameter == null ? "" : requestParameter.RequestId, ex.Message, ex);
                    throw serviceClientException;
                }

            }
            finally
            {
                try
                {
                    if (requestParameter != null)
                    {
                        WriteLog(requestParameter);
                        SaveServiceDependency(requestParameter, watch.ElapsedMilliseconds, isFail);
                    }
                }
                catch
                {
                    // ignored
                }
            }
        }

        /// <summary>
        /// 以Task异步的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <typeparam name="TResponse">响应参数类型</typeparam>
        /// <param name="appId">应用编号，如果不为空则会替换原来打在Request类上的服务资源特性标签中的AppId的值</param>
        /// <param name="request">请求参数</param>
        /// <param name="exceptionResult">当请求发生异常时返回指定的响应值的委托方法</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        /// <returns></returns>
        public static async Task<TResponse> ExecuteTaskAsync<TRequest, TResponse>(string appId, TRequest request,
            Func<Exception, TResponse> exceptionResult = null, int? timeOut = null,
            IDictionary<string, string> headers = null)
        {
            ResourceAttribute resourceAttribute = GetResourceAttribute<TRequest>(appId,request);
            return await ExecuteTaskAsync(request, exceptionResult, timeOut, headers, resourceAttribute);
        }

        /// <summary>
        /// 以Task异步的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <typeparam name="TResponse">响应参数类型</typeparam>
        /// <param name="parameterBuildType">参数构建类型，替换原来打在Request类上的服务资源特性标签中的ParameterBuildType的值</param>
        /// <param name="request">请求参数</param>
        /// <param name="exceptionResult">当请求发生异常时返回指定的响应值的委托方法</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        /// <returns></returns>
        public static async Task<TResponse> ExecuteTaskAsync<TRequest, TResponse>(ParameterBuildType parameterBuildType, TRequest request,
            Func<Exception, TResponse> exceptionResult = null, int? timeOut = null,
            IDictionary<string, string> headers = null)
        {
            ResourceAttribute resourceAttribute = Utils.GetFirstAttribute<ResourceAttribute>(typeof(TRequest), true);
            if (resourceAttribute != null)
                resourceAttribute.ParameterBuildType = parameterBuildType;
            return await ExecuteTaskAsync(request, exceptionResult, timeOut, headers, resourceAttribute);
        }

        /// <summary>
        /// 以Task异步的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <param name="request">请求参数</param>
        /// <param name="exceptionResult">当请求发生异常时返回指定的响应值的委托方法</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        /// <param name="resourceAttribute">服务资源特性标签</param>
        /// <returns></returns>
        public static async Task<string> ExecuteTaskAsync<TRequest>(TRequest request, Func<Exception, string> exceptionResult = null, int? timeOut = null,
            IDictionary<string, string> headers = null, ResourceAttribute resourceAttribute = null)
            where TRequest : new()
        {
            return
                await
                ExecuteTaskAsync<TRequest, TextResponse>(request, exceptionResult == null ? (Func<Exception, TextResponse>)null : (ex) => new TextResponse() { Result = exceptionResult(ex) }, timeOut, headers, resourceAttribute)
                        .ContinueWith(
                            (task) => task.Result == null ? null : task.Result.Result);
        }

        /// <summary>
        /// 以Task异步的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <param name="appId">应用编号，如果不为空则会替换原来打在Request类上的服务资源特性标签中的AppId的值</param>
        /// <param name="request">请求参数</param>
        /// <param name="exceptionResult">当请求发生异常时返回指定的响应值的委托方法</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        /// <returns></returns>
        public static async Task<string> ExecuteTaskAsync<TRequest>(string appId, TRequest request, Func<Exception, string> exceptionResult = null, int? timeOut = null,
            IDictionary<string, string> headers = null)
            where TRequest : new()
        {
            ResourceAttribute resourceAttribute = GetResourceAttribute<TRequest>(appId,request);
            return await ExecuteTaskAsync(request, exceptionResult, timeOut, headers, resourceAttribute);
        }

        /// <summary>
        /// 以Task异步的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <param name="parameterBuildType">参数构建类型，替换原来打在Request类上的服务资源特性标签中的ParameterBuildType的值</param>
        /// <param name="request">请求参数</param>
        /// <param name="exceptionResult">当请求发生异常时返回指定的响应值的委托方法</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        /// <returns></returns>
        public static async Task<string> ExecuteTaskAsync<TRequest>(ParameterBuildType parameterBuildType, TRequest request, Func<Exception, string> exceptionResult = null, int? timeOut = null,
            IDictionary<string, string> headers = null)
            where TRequest : new()
        {
            ResourceAttribute resourceAttribute = Utils.GetFirstAttribute<ResourceAttribute>(typeof(TRequest), true);
            if (resourceAttribute != null)
                resourceAttribute.ParameterBuildType = parameterBuildType;
            return await ExecuteTaskAsync(request, exceptionResult, timeOut, headers, resourceAttribute);
        }

#endif

        /// <summary>
        /// 以同步的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <typeparam name="TResponse">响应参数类型</typeparam>
        /// <param name="request">请求参数</param>
        /// <param name="exceptionResult">当请求发生异常时返回指定的响应值的委托方法</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        /// <param name="resourceAttribute">服务资源特性标签</param>
        /// <returns></returns>
        public static TResponse Execute<TRequest, TResponse>(TRequest request, Func<Exception, TResponse> exceptionResult = null, int? timeOut =null, IDictionary<string, string> headers = null, ResourceAttribute resourceAttribute = null)
            where TResponse : new()
            where TRequest : new()
        {
            if (request == null)
                throw new ArgumentNullException("request");
            
            Stopwatch watch = new Stopwatch();
            watch.Start();

            Type responseType = typeof(TResponse);
            bool isFail = false;
            RequestParameter requestParameter = null;
            try
            {
                if (timeOut == null)
                    timeOut = GetDefaultTimeOut();
                requestParameter = BuildRequestParameter<TRequest, TResponse>(request, headers, resourceAttribute);
                //using (MethodMonitor.New(requestParameter.GetMethodMonitorPath()))
                //{
                    RestClient restClient = new RestClient(requestParameter.ServiceMetadata.Host) { Timeout = timeOut.Value };
                    restClient.UseJsonDotNetDeserializerWrapper();
                    restClient.UserAgent = "Ymatou.ServiceCenter";
                    if (typeof(TextResponse).IsAssignableFrom(responseType))
                    {
                        IRestResponse response = restClient.Execute(requestParameter.RestRequest);
                        response.CheckResponse();
                        object obj = Activator.CreateInstance(responseType, true);
                        ((TextResponse)obj).Result = response.Content;
                        return (TResponse)obj;
                    }
                    else
                    {
                        IRestResponse<TResponse> response = restClient.Execute<TResponse>(requestParameter.RestRequest);
                        response.CheckResponse();
                        return response.Data;
                    }
                //}

            }
            catch (Exception ex)
            {
                isFail = true;
                if (requestParameter != null)
                    WriteLog(requestParameter, ex);

                if (exceptionResult != null)
                {
                    return exceptionResult(ex);
                }
                else
                {
                    ServiceClientException serviceClientException = new ServiceClientException(requestParameter == null ? "" : requestParameter.RequestId, ex.Message, ex);
                    throw serviceClientException;
                }
            }
            finally
            {
                try
                {
                    if (requestParameter != null)
                    {
                        WriteLog(requestParameter);
                        SaveServiceDependency(requestParameter, watch.ElapsedMilliseconds, isFail);
                    }

                }
                catch
                {
                    // ignored
                }
                finally
                {
                    watch.Stop();
                }
            }

        }

        /// <summary>
        /// 以同步的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <typeparam name="TResponse">响应参数类型</typeparam>
        /// <param name="appId">应用编号，如果不为空则会替换原来打在Request类上的服务资源特性标签中的AppId的值</param>
        /// <param name="request">请求参数</param>
        /// <param name="exceptionResult">当请求发生异常时返回指定的响应值的委托方法</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        /// <returns></returns>
        public static TResponse Execute<TRequest, TResponse>(string appId, TRequest request, Func<Exception, TResponse> exceptionResult = null, int? timeOut = null,
            IDictionary<string, string> headers = null)
            where TResponse : new()
            where TRequest : new()
        {
            ResourceAttribute resourceAttribute = GetResourceAttribute<TRequest>(appId,request);
            return Execute(request, exceptionResult, timeOut, headers, resourceAttribute);
        }

        /// <summary>
        /// 以同步的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <typeparam name="TResponse">响应参数类型</typeparam>
        /// <param name="parameterBuildType">参数构建类型，替换原来打在Request类上的服务资源特性标签中的ParameterBuildType的值</param>
        /// <param name="request">请求参数</param>
        /// <param name="exceptionResult">当请求发生异常时返回指定的响应值的委托方法</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        /// <returns></returns>
        public static TResponse Execute<TRequest, TResponse>(ParameterBuildType parameterBuildType,
            TRequest request, Func<Exception, TResponse> exceptionResult = null, int? timeOut = null,
            IDictionary<string, string> headers = null)
            where TResponse : new()
            where TRequest : new()
        {
            ResourceAttribute resourceAttribute = Utils.GetFirstAttribute<ResourceAttribute>(typeof(TRequest), true);
            if (resourceAttribute != null)
                resourceAttribute.ParameterBuildType = parameterBuildType;
            return Execute(request, exceptionResult, timeOut, headers, resourceAttribute);
        }


        /// <summary>
        /// 以同步的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <param name="request">响应参数类型</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        /// <param name="resourceAttribute">服务资源特性标签</param>
        /// <returns></returns>
        public static string Execute<TRequest>(TRequest request, int? timeOut = null, IDictionary<string, string> headers = null, ResourceAttribute resourceAttribute = null)
            where TRequest : new()
        {
            TextResponse textResponse = Execute<TRequest, TextResponse>(request, null, timeOut, headers, resourceAttribute);
            return textResponse == null ? null : textResponse.Result;
        }

        /// <summary>
        /// 以同步的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <param name="appId">应用编号，如果不为空则会替换原来打在Request类上的服务资源特性标签中的AppId的值</param>
        /// <param name="request">响应参数类型</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        /// <returns></returns>
        public static string Execute<TRequest>(string appId, TRequest request, int? timeOut = null,
            IDictionary<string, string> headers = null)
            where TRequest : new()
        {
            ResourceAttribute resourceAttribute = GetResourceAttribute<TRequest>(appId,request);
            return Execute(request, timeOut, headers, resourceAttribute);
        }

        /// <summary>
        /// 以同步的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <param name="parameterBuildType">参数构建类型，替换原来打在Request类上的服务资源特性标签中的ParameterBuildType的值</param>
        /// <param name="request">响应参数类型</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        /// <returns></returns>
        public static string Execute<TRequest>(ParameterBuildType parameterBuildType, TRequest request, int? timeOut = null,
            IDictionary<string, string> headers = null)
            where TRequest : new()
        {
            ResourceAttribute resourceAttribute = Utils.GetFirstAttribute<ResourceAttribute>(typeof(TRequest), true);
            if (resourceAttribute != null)
                resourceAttribute.ParameterBuildType = parameterBuildType;
            return Execute(request, timeOut, headers, resourceAttribute);
        }


        /// <summary>
        /// 以异步回调的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <typeparam name="TResponse">响应参数类型</typeparam>
        /// <param name="request">请求参数</param>
        /// <param name="responseCallback">响应回调处理方法</param>
        /// <param name="exceptionCallback">异常处理方法</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        /// <param name="resourceAttribute">服务资源特性标签</param>
        public static void ExecuteAsync<TRequest, TResponse>(TRequest request, Action<TResponse> responseCallback,
            Action<Exception> exceptionCallback, int? timeOut = null, IDictionary<string, string> headers = null, ResourceAttribute resourceAttribute = null)
            where TResponse : new()
            where TRequest : new()
        {
            bool isFail = false;
            RequestParameter requestParameter = null;
            try
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                if (timeOut == null)
                    timeOut = GetDefaultTimeOut();
                requestParameter = BuildRequestParameter<TRequest, TResponse>(request, headers, resourceAttribute);
                //MethodMonitor methodMonitor = MethodMonitor.New(requestParameter.GetMethodMonitorPath());
                RestClient restClient = new RestClient(requestParameter.ServiceMetadata.Host) { Timeout = timeOut.Value };
                restClient.UseJsonDotNetDeserializerWrapper();
                restClient.UserAgent = "Ymatou.ServiceCenter";
                Type responseType = typeof(TResponse);

                if (typeof(TextResponse).IsAssignableFrom(responseType))
                {
                    restClient.ExecuteAsync(requestParameter.RestRequest, (restResponse, handle) =>
                    {
                        try
                        {
                            if (responseCallback == null) return;
                            restResponse.CheckResponse();
                            object obj = Activator.CreateInstance(responseType, true);
                            ((TextResponse)obj).Result = restResponse.Content;
                            responseCallback((TResponse)obj);
                        }
                        catch (Exception ex)
                        {
                            isFail = true;
                            try
                            {
                                if (exceptionCallback != null)
                                {
                                    ServiceClientException serviceClientException = new ServiceClientException(requestParameter == null ? "" : requestParameter.RequestId, ex.Message, ex);
                                    exceptionCallback(serviceClientException);
                                }
                            }
                            catch (Exception e)
                            {
                                WriteLog(requestParameter, e);
                            }
                            WriteLog(requestParameter, ex);
                        }
                        finally
                        {
                            try
                            {
                                SaveServiceDependency(requestParameter, watch.ElapsedMilliseconds, isFail);
                            }
                            catch
                            {
                                // ignored
                            }
                            finally
                            {
                                watch.Stop();
                                //methodMonitor.Dispose();
                            }
                        }
                    });

                }
                else
                {
                    restClient.ExecuteAsync<TResponse>(requestParameter.RestRequest, (restResponse, handle) =>
                    {
                        try
                        {
                            if (responseCallback == null) return;
                            restResponse.CheckResponse();
                            responseCallback(restResponse.Data);
                        }
                        catch (Exception ex)
                        {
                            isFail = true;
                            try
                            {
                                if (exceptionCallback != null)
                                {
                                    ServiceClientException serviceClientException = new ServiceClientException(requestParameter == null ? "" : requestParameter.RequestId, ex.Message, ex);
                                    exceptionCallback(serviceClientException);
                                }

                            }
                            catch (Exception e)
                            {
                                WriteLog(requestParameter, e);
                            }
                            WriteLog(requestParameter, ex);
                        }
                        finally
                        {
                            try
                            {
                                SaveServiceDependency(requestParameter, watch.ElapsedMilliseconds, isFail);
                            }
                            catch
                            {
                                // ignored
                            }
                            finally
                            {
                                watch.Stop();
                                //methodMonitor.Dispose();
                            }
                        }
                    });
                }
            }
            catch (Exception ex)
            {
                if (requestParameter != null)
                    WriteLog(requestParameter, ex);
                ServiceClientException serviceClientException = new ServiceClientException(requestParameter == null ? "" : requestParameter.RequestId, ex.Message, ex);
                throw serviceClientException;
            }
            finally
            {
                try
                {
                    if (requestParameter != null)
                    {
                        WriteLog(requestParameter);
                    }
                }
                catch
                {
                    // ignored
                }
            }
        }

        /// <summary>
        /// 以异步回调的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <typeparam name="TResponse">响应参数类型</typeparam>
        /// <param name="appId">应用编号，如果不为空则会替换原来打在Request类上的服务资源特性标签中的AppId的值</param>
        /// <param name="request">请求参数</param>
        /// <param name="responseCallback">响应回调处理方法</param>
        /// <param name="exceptionCallback">异常处理方法</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        public static void ExecuteAsync<TRequest, TResponse>(string appId, TRequest request, Action<TResponse> responseCallback,
            Action<Exception> exceptionCallback, int? timeOut = null, IDictionary<string, string> headers = null)
            where TResponse : new()
            where TRequest : new()
        {
            ResourceAttribute resourceAttribute = GetResourceAttribute<TRequest>(appId,request);
            ExecuteAsync(request, responseCallback, exceptionCallback, timeOut, headers, resourceAttribute);
        }

        /// <summary>
        /// 以异步回调的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <typeparam name="TResponse">响应参数类型</typeparam>
        /// <param name="parameterBuildType">参数构建类型，替换原来打在Request类上的服务资源特性标签中的ParameterBuildType的值</param>
        /// <param name="request">请求参数</param>
        /// <param name="responseCallback">响应回调处理方法</param>
        /// <param name="exceptionCallback">异常处理方法</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        public static void ExecuteAsync<TRequest, TResponse>(ParameterBuildType parameterBuildType, TRequest request,
            Action<TResponse> responseCallback,
            Action<Exception> exceptionCallback, int? timeOut = null, IDictionary<string, string> headers = null)
            where TResponse : new()
            where TRequest : new()
        {
            ResourceAttribute resourceAttribute = Utils.GetFirstAttribute<ResourceAttribute>(typeof(TRequest), true);
            if (resourceAttribute != null)
                resourceAttribute.ParameterBuildType = parameterBuildType;
            ExecuteAsync(request, responseCallback, exceptionCallback, timeOut, headers, resourceAttribute);
        }

        /// <summary>
        /// 以异步回调的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <param name="request">请求参数</param>
        /// <param name="responseCallback">响应回调处理方法</param>
        /// <param name="exceptionCallback">异常处理方法</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        /// <param name="resourceAttribute">服务资源特性标签</param>
        public static void ExecuteAsync<TRequest>(TRequest request, Action<string> responseCallback,
            Action<Exception> exceptionCallback, int? timeOut = null, IDictionary<string, string> headers = null, ResourceAttribute resourceAttribute = null)
            where TRequest : new()
        {
            ExecuteAsync<TRequest, TextResponse>(request, (textResponse) =>
            {
                if (responseCallback != null && textResponse != null)
                {
                    responseCallback(textResponse.Result);
                }
            }, exceptionCallback, timeOut, headers, resourceAttribute);
        }

        /// <summary>
        /// 以异步回调的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <param name="appId">应用编号，如果不为空则会替换原来打在Request类上的服务资源特性标签中的AppId的值</param>
        /// <param name="request">请求参数</param>
        /// <param name="responseCallback">响应回调处理方法</param>
        /// <param name="exceptionCallback">异常处理方法</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        public static void ExecuteAsync<TRequest>(string appId, TRequest request, Action<string> responseCallback,
            Action<Exception> exceptionCallback, int? timeOut = null, IDictionary<string, string> headers = null)
            where TRequest : new()
        {
            ResourceAttribute resourceAttribute = GetResourceAttribute<TRequest>(appId,request);
            ExecuteAsync(request, responseCallback, exceptionCallback, timeOut, headers, resourceAttribute);
        }

        /// <summary>
        /// 以异步回调的方式执行请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数类型</typeparam>
        /// <param name="parameterBuildType">参数构建类型，替换原来打在Request类上的服务资源特性标签中的ParameterBuildType的值</param>
        /// <param name="request">请求参数</param>
        /// <param name="responseCallback">响应回调处理方法</param>
        /// <param name="exceptionCallback">异常处理方法</param>
        /// <param name="timeOut">超时时间值（毫秒）</param>
        /// <param name="headers">http头信息</param>
        public static void ExecuteAsync<TRequest>(ParameterBuildType parameterBuildType, TRequest request, Action<string> responseCallback,
            Action<Exception> exceptionCallback, int? timeOut = null, IDictionary<string, string> headers = null)
            where TRequest : new()
        {
            ResourceAttribute resourceAttribute = Utils.GetFirstAttribute<ResourceAttribute>(typeof(TRequest), true);
            if (resourceAttribute != null)
                resourceAttribute.ParameterBuildType = parameterBuildType;
            ExecuteAsync(request, responseCallback, exceptionCallback, timeOut, headers, resourceAttribute);
        }
    }
}
